# You should download and unzip "fastBCR-main.zip" from "https://github.com/ZhangLabTJU/fastBCR" and set it as working directory before running pipeline.
# knitr::opts_knit$set(root.dir = "THE/PATH/TO/FASTBCR/FOLDER")
knitr::opts_knit$set(root.dir = "~/Documents/Rpackage/fastBCR-main") # Replace with your path.
library(fastBCR)
Loading required package: dplyr
Warning: package ‘dplyr’ was built under R version 4.2.3
Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Loading required package: ggplot2
Loading required package: ggmsa
Registered S3 methods overwritten by 'ggalt':
  method                  from   
  grid.draw.absoluteGrob  ggplot2
  grobHeight.absoluteGrob ggplot2
  grobWidth.absoluteGrob  ggplot2
  grobX.absoluteGrob      ggplot2
  grobY.absoluteGrob      ggplot2
ggmsa v1.4.0  Document: http://yulab-smu.top/ggmsa/

If you use ggmsa in published research, please cite:
L Zhou, T Feng, S Xu, F Gao, TT Lam, Q Wang, T Wu, H Huang, L Zhan, L Li, Y Guan, Z Dai*, G Yu* ggmsa: a visual exploration tool for multiple sequence alignment and associated data. Briefings in Bioinformatics. DOI:10.1093/bib/bbac222
Loading required package: msa
Loading required package: Biostrings
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:dplyr’:

    combine, intersect, setdiff, union

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, aperm, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find, get, grep, grepl,
    intersect, is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply,
    setdiff, sort, table, tapply, union, unique, unsplit, which.max, which.min

Loading required package: S4Vectors
Loading required package: stats4

Attaching package: ‘S4Vectors’

The following objects are masked from ‘package:dplyr’:

    first, rename

The following objects are masked from ‘package:base’:

    expand.grid, I, unname

Loading required package: IRanges

Attaching package: ‘IRanges’

The following objects are masked from ‘package:dplyr’:

    collapse, desc, slice

Loading required package: XVector
Loading required package: GenomeInfoDb

Attaching package: ‘Biostrings’

The following object is masked from ‘package:base’:

    strsplit

Loading required package: statnet
Loading required package: tergm
Loading required package: ergm
Warning: package ‘ergm’ was built under R version 4.2.3Loading required package: network
Warning: package ‘network’ was built under R version 4.2.3
‘network’ 1.18.2 (2023-12-04), part of the Statnet Project
* ‘news(package="network")’ for changes since last version
* ‘citation("network")’ for citation information
* ‘https://statnet.org’ for help, support, and other information


‘ergm’ 4.6.0 (2023-12-17), part of the Statnet Project
* ‘news(package="ergm")’ for changes since last version
* ‘citation("ergm")’ for citation information
* ‘https://statnet.org’ for help, support, and other information

‘ergm’ 4 is a major update that introduces some backwards-incompatible changes. Please type ‘news(package="ergm")’ for a list of major changes.

Loading required package: networkDynamic
Warning: package ‘networkDynamic’ was built under R version 4.2.3
‘networkDynamic’ 0.11.4 (2023-12-10?), part of the Statnet Project
* ‘news(package="networkDynamic")’ for changes since last version
* ‘citation("networkDynamic")’ for citation information
* ‘https://statnet.org’ for help, support, and other information

Registered S3 method overwritten by 'tergm':
  method                   from
  simulate_formula.network ergm

‘tergm’ 4.2.0 (2023-05-30), part of the Statnet Project
* ‘news(package="tergm")’ for changes since last version
* ‘citation("tergm")’ for citation information
* ‘https://statnet.org’ for help, support, and other information


Attaching package: ‘tergm’

The following object is masked from ‘package:ergm’:

    snctrl

Loading required package: ergm.count

‘ergm.count’ 4.1.1 (2022-05-24), part of the Statnet Project
* ‘news(package="ergm.count")’ for changes since last version
* ‘citation("ergm.count")’ for citation information
* ‘https://statnet.org’ for help, support, and other information

Loading required package: sna
Warning: package ‘sna’ was built under R version 4.2.3Loading required package: statnet.common

Attaching package: ‘statnet.common’

The following object is masked from ‘package:ergm’:

    snctrl

The following object is masked from ‘package:Biostrings’:

    order

The following object is masked from ‘package:XVector’:

    order

The following object is masked from ‘package:IRanges’:

    order

The following object is masked from ‘package:S4Vectors’:

    order

The following object is masked from ‘package:BiocGenerics’:

    order

The following objects are masked from ‘package:base’:

    attr, order

sna: Tools for Social Network Analysis
Version 2.7-2 created on 2023-12-05.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.

Loading required package: tsna

‘statnet’ 2019.6 (2019-06-13), part of the Statnet Project
* ‘news(package="statnet")’ for changes since last version
* ‘citation("statnet")’ for citation information
* ‘https://statnet.org’ for help, support, and other information
### Fast BCR clonal family inference
## 1. Data loading
# Path to the "COVID"/"HC" folder in the "example" folder in fastBCR project.
COVID_folder_path <- "example/COVID"
HC_folder_path <- "example/HC"
# Load files from "COVID_folder_path"/"HC_folder_path" into list.
# The storage format of data can be in "csv", "tsv", or "Rdata" format.
# The compressed files in the above storage format in "7zip", "cab", "cpio", "iso9660", "lha", "mtree", "shar", "rar", "raw", "tar", "xar", "zip", "warc" format can also be read in.
COVID_raw_data_list <- data.load(folder_path = COVID_folder_path, storage_format = "csv")

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have loaded 5 samples
Sample 'COVID_01' contains 298430 sequences
Sample 'COVID_02' contains 326522 sequences
Sample 'COVID_03' contains 317836 sequences
Sample 'COVID_04' contains 294034 sequences
Sample 'COVID_05' contains 372852 sequences
HC_raw_data_list <- data.load(folder_path = HC_folder_path, storage_format = "csv")

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have loaded 5 samples
Sample 'HC_01' contains 152349 sequences
Sample 'HC_02' contains 132252 sequences
Sample 'HC_03' contains 124646 sequences
Sample 'HC_04' contains 97251 sequences
Sample 'HC_05' contains 110364 sequences
## 2. Data preprocessing
# Preprocessing of raw data to meet the input requirements for clonal family inference.
# The input data needs to contain essential columns including "v_call" (V gene with or without allele), "j_call" (J gene with or without allele) and "junction_aa" (amino acid translation of the junction). 
# The "junction" (junction region nucleotide sequence, where the junction is defined as the CDR3 plus the two flanking conserved codons) column is needed for phylogenetic tree construction and SHM-related analysis
# The "c_call" (constant region gene with or without allele) column is needed for isotype-related analysis.
# During data preprocessing, only productive sequences whose junction amino acid lengths between 9 and 26 are reserved.
# The "count_col_name" parameter represents the column name for the count of each sequence.
# It defaults to "NA" which means the original count of the sequence is not taken into account.
# Sequences with the same "v_call", "j_call" and "junction_aa" are considered to be the same clonotype and are merged into one row in processed data.
# The column "clonotype_count" is the number of sequences belonging to each clonotype.
# The column "clone_count" is the sum of the counts (calculated based on "count_col_name" parameter) of the sequences belonging to each clonotype.
# The column "clone_fre" is the frequency version of "clone_count".
# The information of the sequence with the highest count in each clonotype is retained.
# The list "index_match" saves the original indexes of sequences for each clonotype.
COVID_pro_data_list <- data.preprocess(data_list = COVID_raw_data_list, count_col_name = "consensus_count")

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have preprocessed 5 samples
Sample 'COVID_01' contains 88502 unique clonotypes
Sample 'COVID_02' contains 101239 unique clonotypes
Sample 'COVID_03' contains 85374 unique clonotypes
Sample 'COVID_04' contains 108583 unique clonotypes
Sample 'COVID_05' contains 119386 unique clonotypes
HC_pro_data_list <- data.preprocess(data_list = HC_raw_data_list, count_col_name = "consensus_count")

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have preprocessed 5 samples
Sample 'HC_01' contains 105364 unique clonotypes
Sample 'HC_02' contains 96186 unique clonotypes
Sample 'HC_03' contains 91897 unique clonotypes
Sample 'HC_04' contains 74450 unique clonotypes
Sample 'HC_05' contains 93030 unique clonotypes
## 3. BCR clonal family inference
# Fast clonal family inference from preprocessed data.
# The "cluster_thre" parameter represents minimal clustering criteria (minimum number of sequences needed to form a cluster) and defaults to 3.
# For high efficiency, the "cluster_thre" is increased by 1 for every 100,000 entries of input data.
# The "overlap_thre" parameter represents overlap coefficient threshold for merging two clusters, selectable range (0,1) and defaults to 0.1.
# Lower "overlap_thre" may lead to overclustering while higher thresholds may lead to the split of clonal families.
# The "consensus_thre" parameter represents the consensus score threshold for filtering candidates and defaults to 0.8.
# A higher "consensus_thre" means stricter inference of the cluster.
COVID_clusters_list <- data.BCR.clusters(pro_data_list = COVID_pro_data_list, cluster_thre = 3, overlap_thre = 0.1, consensus_thre = 0.8)

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have clustered 5 samples
Sample 'COVID_01' contains 4903 clonal families
Sample 'COVID_02' contains 3899 clonal families
Sample 'COVID_03' contains 4614 clonal families
Sample 'COVID_04' contains 3145 clonal families
Sample 'COVID_05' contains 3303 clonal families
HC_clusters_list <- data.BCR.clusters(pro_data_list = HC_pro_data_list, cluster_thre = 3, overlap_thre = 0.1, consensus_thre = 0.8)

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have clustered 5 samples
Sample 'HC_01' contains 799 clonal families
Sample 'HC_02' contains 2056 clonal families
Sample 'HC_03' contains 2305 clonal families
Sample 'HC_04' contains 1204 clonal families
Sample 'HC_05' contains 1624 clonal families
### Downstream analysis
## 1. Single cluster analysis
## 1.1 Conserved motifs distribution
# Visualization of multiple sequence alignment (MSA) of junction sequences within a cluster.
# The parameter "index" allows you to choose a cluster for visualization.
# The parameter "type" can be "AA" for amino acid or "DNA" for deoxyribonucleic acid.
# The parameter "raw_data" is the raw data which the clonal families inferred from.
# It is needed if you want to plot the MSA of 'DNA' sequences.
# fastBCR will retrieve all the DNA sequences, which can be multiple sequences due to the degeneracy of codons, that correspond to the amino acid sequence of each clonotype from the raw data
COVID_01_clusters <- COVID_clusters_list$COVID_01
HC_01_clusters <- HC_clusters_list$HC_01
msa.plot(bcr_clusters = COVID_01_clusters, index = 200, type = "AA")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

msa.plot(bcr_clusters = COVID_01_clusters, index = 200, type = "DNA", raw_data = COVID_raw_data_list$COVID_01)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

## 1.2 Sequence logo
# Visualization of sequence logo of junction sequences within a cluster.
seqlogo.plot(bcr_clusters = COVID_01_clusters, index = 200, type = "AA")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

seqlogo.plot(bcr_clusters = COVID_01_clusters, index = 200, type = "DNA", raw_data = COVID_raw_data_list$COVID_01)
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.Coordinate system already present. Adding new coordinate system, which will replace the existing one.

## 1.3 Evolutionary tree
# Reconstruct a B cell lineage tree with minimum spanning tree and genotype abundances using ClonalTree.
# The junction of BCR sequences within a cluster will be written as "ClonalFamily_index.fasta" in "ClonalTree/Examples/input" folder.
# ClonalTree uses Python for compilation, so it needs the absolute path of the Python interpreter in the "python_path" parameter.
# ClonalTree returns two files in the "ClonalTree/Examples/output" folder.
# ClonalFamily_index.nk: the reconstructed BCR lineage tree in newick format.
# ClonalFamily_index.nk.csv: a table in csv format, containing the parent relationship and cost.
clonal.tree.generation(bcr_clusters = COVID_01_clusters, index = 200, raw_data = COVID_raw_data_list$COVID_01, python_path = "/Users/wangkaixuan/anaconda3/bin/python") # Replace with your path
use default substitution matrix
Parameter setting = useAbundance:  False ; revision:  True ; trim: False
done
# You can use the clonal.tree.plot() function to visualize the evolutionary tree in R.
# The consensus sequence of the cluster is used as the root node of the tree.
# Orange points represent nodes and blue points represent tips.
# The x-axis shows the absolute genetic distance.
clonal.tree.plot(nk_path = "ClonalTree/Examples/output/ClonalFamily_200.abRT.nk")

## 1.4 Class switch recombination (CSR) network
# Visualization of isotype co-occurrence within clusters within a cluster
# Circle size represents the number of sequences carrying a given isotype.
# Lines connecting two circles indicate the enrichment level of observing switches in the two corresponding immunoglobulin subclasses.
# The enrichment level is the ratio of observed and expected switches if immunoglobulin isotypes are assumed to be independently distributed among cluster.
# Matrix of values of connected edges between clustered sequences in different isotypes is printed.
CSR.cluster.plot(bcr_clusters = COVID_01_clusters, index = 50)
      IGHM IGHD IGHG3 IGHG1 IGHA1 IGHG2 IGHG4 IGHE IGHA2
IGHM     0    0     0     1     1     0     0    0     0
IGHD     0    0     0     0     0     0     0    0     0
IGHG3    0    0     0     0     0     0     0    0     0
IGHG1    1    0     0     0     1     0     0    0     0
IGHA1    1    0     0     1     0     0     0    0     0
IGHG2    0    0     0     0     0     0     0    0     0
IGHG4    0    0     0     0     0     0     0    0     0
IGHE     0    0     0     0     0     0     0    0     0
IGHA2    0    0     0     0     0     0     0    0     0

## 2. Single sample/group analysis
## 2.1 Classification of clustered and unclustered sequences
# Merge all the clustered sequences in each sample into "clustered_seqs".
# Merge all the unclustered sequences in each sample into "unclustered_seqs".
COVID_seqs_list <- Clustered.seqs(pro_data_list = COVID_pro_data_list, clusters_list = COVID_clusters_list)

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have classified clustered and unclustered sequences for 5 samples
Sample 'COVID_01' contains 43862 clustered sequences and 44640 unclustered sequences
Sample 'COVID_02' contains 43901 clustered sequences and 57338 unclustered sequences
Sample 'COVID_03' contains 46536 clustered sequences and 38838 unclustered sequences
Sample 'COVID_04' contains 35007 clustered sequences and 73576 unclustered sequences
Sample 'COVID_05' contains 43627 clustered sequences and 75759 unclustered sequences
HC_seqs_list <- Clustered.seqs(pro_data_list = HC_pro_data_list, clusters_list = HC_clusters_list)

  |                                                                                                                                    
  |                                                                                                                              |   0%
  |                                                                                                                                    
  |=========================                                                                                                     |  20%
  |                                                                                                                                    
  |==================================================                                                                            |  40%
  |                                                                                                                                    
  |============================================================================                                                  |  60%
  |                                                                                                                                    
  |=====================================================================================================                         |  80%
  |                                                                                                                                    
  |==============================================================================================================================| 100%
You have classified clustered and unclustered sequences for 5 samples
Sample 'HC_01' contains 5386 clustered sequences and 99978 unclustered sequences
Sample 'HC_02' contains 8582 clustered sequences and 87604 unclustered sequences
Sample 'HC_03' contains 9914 clustered sequences and 81983 unclustered sequences
Sample 'HC_04' contains 4946 clustered sequences and 69504 unclustered sequences
Sample 'HC_05' contains 6545 clustered sequences and 86485 unclustered sequences
## 2.2 Summary of clusters from a sample
# Summarize the number of clusters, the average size of clusters and the proportion of clustered sequences.
COVID_clusters_summary <- Clusters.summary(pro_data_list = COVID_pro_data_list, clusters_list = COVID_clusters_list)
HC_clusters_summary <- Clusters.summary(pro_data_list = HC_pro_data_list, clusters_list = HC_clusters_list)
COVID_01_summary <- COVID_clusters_summary$COVID_01
print('Summary of COVID_01:')
[1] "Summary of COVID_01:"
print(COVID_01_summary)
$`number of clusters`
[1] 4903

$`average size of clusters`
[1] 8.95

$`number of clustered seqs`
[1] 43862

$`number of all seqs`
[1] 88502

$`proportion of clustered sequences`
[1] "49.56%"
HC_01_summary <- HC_clusters_summary$HC_01
print('Summary of HC_01:')
[1] "Summary of HC_01:"
print(HC_01_summary)
$`number of clusters`
[1] 799

$`average size of clusters`
[1] 6.74

$`number of clustered seqs`
[1] 5386

$`number of all seqs`
[1] 105364

$`proportion of clustered sequences`
[1] "5.11%"
## 2.3 Visualization of clusters from a sample
# Point diagram showing clusters from a sample where a circle represents a cluster.
# The size and color of the circle represents the size of the cluster.
# The parameter "index" represents the index of the sample for visualization.
Clusters.visualization(pro_data_list = COVID_pro_data_list, clusters_list = COVID_clusters_list, index = 1)

Clusters.visualization(pro_data_list = HC_pro_data_list, clusters_list = HC_clusters_list, index = 1)

## 2.4 V/J gene usage
## 2.4.1 Pie chart: V/J gene
# Pie chart showing the gene usage frequency of clustered sequences.
# The top ten most frequent genes are shown, and the rest are represented by "others".
# The parameter "colname" can be "v_call" for V gene or "j_call" for J gene.
# (1) single sample
COVID_01_clustered_seqs <- COVID_seqs_list$clustered_seqs$COVID_01
COVID_01_unclustered_seqs <- COVID_seqs_list$unclustered_seqs$COVID_01
HC_01_clustered_seqs <- HC_seqs_list$clustered_seqs$HC_01
pie.freq.plot(clustered_seqs = COVID_01_clustered_seqs, colname = "v_call")

pie.freq.plot(clustered_seqs = HC_01_clustered_seqs, colname = "v_call")

# (2) multiple samples in a group.
# All the clustered/unclustered sequences from multiple samples in a group can be merged.
COVID_all_clustered_seqs <- NULL
for(i in 1:length(COVID_seqs_list$clustered_seqs)){
  COVID_all_clustered_seqs <- rbind(COVID_all_clustered_seqs, COVID_seqs_list$clustered_seqs[[i]])
}
COVID_all_unclustered_seqs <- NULL
for(i in 1:length(COVID_seqs_list$unclustered_seqs)){
  COVID_all_unclustered_seqs <- rbind(COVID_all_unclustered_seqs, COVID_seqs_list$unclustered_seqs[[i]])
}
HC_all_clustered_seqs <- NULL
for(i in 1:length(HC_seqs_list$clustered_seqs)){
  HC_all_clustered_seqs <- rbind(HC_all_clustered_seqs, HC_seqs_list$clustered_seqs[[i]])
}
HC_all_unclustered_seqs <- NULL
for(i in 1:length(HC_seqs_list$unclustered_seqs)){
  HC_all_unclustered_seqs <- rbind(HC_all_unclustered_seqs, HC_seqs_list$unclustered_seqs[[i]])
}
pie.freq.plot(clustered_seqs = COVID_all_clustered_seqs, colname = "v_call")

pie.freq.plot(clustered_seqs = HC_all_clustered_seqs, colname = "v_call")

## 2.4.2 Heatmap: V-J gene pair
# Heatmap showing the V-J gene pair frequency of clustered sequences
# (1) single sample.
vjpair.sample.plot(clustered_seqs = COVID_01_clustered_seqs)
Using Freq as value column: use value.var to override.

# (2) multiple samples in a group.
vjpair.sample.plot(clustered_seqs = COVID_all_clustered_seqs)
Using Freq as value column: use value.var to override.

vjpair.sample.plot(clustered_seqs = HC_all_clustered_seqs)
Using Freq as value column: use value.var to override.

## 2.5 Junction length distribution
# 2.5.1 Histogram and density plot showing the length distribution of junction amino acid of clustered sequences.
# (1) single sample
len.sample.plot(clustered_seqs = COVID_01_clustered_seqs)

# (2) multiple samples in a group
len.sample.plot(clustered_seqs = COVID_all_clustered_seqs)

# 2.5.2 Density ridges showing the length distributions of junction amino acid of clustered sequences and unclustered sequences.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
# (1) single sample
len.clustered.plot(clustered_seqs = COVID_01_clustered_seqs,
                   unclustered_seqs = COVID_01_unclustered_seqs)
[1] "p-value: <2e-16"

# (2) multiple samples in a group
len.clustered.plot(clustered_seqs = COVID_all_clustered_seqs,
                   unclustered_seqs = COVID_all_unclustered_seqs)
[1] "p-value: <2e-16"

## 2.6 Class switch recombination (CSR) network
# Visualization of isotype co-occurrence within clusters from a sample.
# Circle size represents the number of sequences carrying a given isotype.
# Lines connecting two circles indicate the enrichment level of observing switches in the two corresponding immunoglobulin subclasses.
# The enrichment level is the ratio of observed and expected switches if immunoglobulin isotypes are assumed to be independently distributed among cluster.
# Matrix of values of connected edges between clustered sequences in different isotypes is printed.
CSR.sample.plot(bcr_clusters = COVID_01_clusters)
      IGHM IGHD IGHG3 IGHG1 IGHA1 IGHG2 IGHG4 IGHE IGHA2
IGHM     0   91    39   465   256    79     1    0    51
IGHD    91    0     4    45    28     8     0    0     7
IGHG3   39    4     0    96    41    32     0    0     6
IGHG1  465   45    96     0   332   165     1    1    50
IGHA1  256   28    41   332     0    80     1    0   108
IGHG2   79    8    32   165    80     0     2    0    56
IGHG4    1    0     0     1     1     2     0    0     0
IGHE     0    0     0     1     0     0     0    0     0
IGHA2   51    7     6    50   108    56     0    0     0

CSR.sample.plot(bcr_clusters = HC_01_clusters)
      IGHM IGHD IGHG3 IGHG1 IGHA1 IGHG2 IGHG4 IGHE IGHA2
IGHM     0  241    11    25    58    51     1    0    36
IGHD   241    0     5    32    42    32     1    0    25
IGHG3   11    5     0    12    16    19     1    0     8
IGHG1   25   32    12     0    72    58     1    0    34
IGHA1   58   42    16    72     0    97     2    0    75
IGHG2   51   32    19    58    97     0     4    0    72
IGHG4    1    1     1     1     2     4     0    0     2
IGHE     0    0     0     0     0     0     0    0     0
IGHA2   36   25     8    34    75    72     2    0     0

### 2.7 Neutralizing antibody (NAb) sequence query
## 2.7.1 Load the public antibody database to get the information of neutralizing antibody (NAb) sequences.
# Here is an example of the Coronavirus Antibody Database (CoV-AbDab).
CoV_AbDab <- read.csv("example/CoV-AbDab_130623.csv")
## 2.7.2 NAb query
# Query the corresponding sequence from the public antibody database.
# The parameter "method" represents the CDRH3 matching method.
# It can be "NA" for perfect match, "hamming" for hamming distance or "lv" for Levenshtein distance. Defaults to "NA".
# The parameter "species" can be "Mouse" or "Human". Defaults to "Human".
# The parameter "maxDist" represents the maximum distance allowed for matching when the argument "method" is "hamming" or "lv". Defaults to "NA".
# example to perfect match
human_perfect_match <- NAb.query(bcr_clusters = COVID_01_clusters, AbDab = CoV_AbDab, method = NA, maxDist = NA, species = "Human")
[1] "The query result has 26 rows."
head(human_perfect_match)
# example with perfect match in "Mouse" species
mouse_perfect_match <- NAb.query(bcr_clusters = COVID_01_clusters, AbDab = CoV_AbDab, method = NA, maxDist = NA, species = "Mouse")
[1] "The query result is empty."
# example with fuzzy matching with "hamming" method and max distance of 1
human_hamming_1_match <- NAb.query(bcr_clusters = COVID_01_clusters, AbDab = CoV_AbDab, method = "hamming", maxDist = 1, species = "Human")
[1] "The query result has 220 rows."
## 3. Inter group analysis
## 3.1 V/J gene usage
## 3.1.1 Boxplot: V/J gene
# Boxplot showing the V/J gene usage of the clustered sequences between two groups.
# The parameter "colname" can be "v_call" for V gene or "j_call" for J gene.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
gene.fre.plot(group1_seqs_list = COVID_seqs_list,
              group1_all_clustered_seqs = COVID_all_clustered_seqs,
              group1_label = "COVID-19",
              group2_seqs_list = HC_seqs_list,
              group2_all_clustered_seqs = HC_all_clustered_seqs,
              group2_label = "HC",
              colname = "v_call")

gene.fre.plot(group1_seqs_list = COVID_seqs_list,
              group1_all_clustered_seqs = COVID_all_clustered_seqs,
              group1_label = "COVID-19",
              group2_seqs_list = HC_seqs_list,
              group2_all_clustered_seqs = HC_all_clustered_seqs,
              group2_label = "HC",
              colname = "j_call")

## 3.1.2 Heatmap: V-J gene pair
# Heatmap showing the fold change of V-J gene pair frequency of clustered sequences between two groups.
# Log fold change (log FC) is calculated as the log2 ratio of the average values between group1 and group2 samples.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
# FDR correction was performed with the Benjamini–Hochberg procedure.
# The V-J gene pair frequency of samples with a frequency less than the minimum value (1‰) is set to the minimum value.
vjpair.group.plot(group1_seqs_list = COVID_seqs_list,
                  group1_all_clustered_seqs = COVID_all_clustered_seqs,
                  group1_label = "COVID-19",
                  group2_seqs_list = HC_seqs_list,
                  group2_all_clustered_seqs = HC_all_clustered_seqs,
                  group2_label = "HC")

## 3.2 Junction length
# Density ridges showing the length distribution of junction amino acid of clustered sequences between two groups.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
len.group.plot(group1_all_clustered_seqs = COVID_all_clustered_seqs, group1_label = "COVID-19",
               group2_all_clustered_seqs = HC_all_clustered_seqs, group2_label = "HC")
[1] "p-value: <2e-16"

## 3.3 Diversity analysis
## 3.3.1 Number and size of clusters
# Bubble plot showing the size and number of clusters between two groups.
clu.size.plot(clusters_list1 = COVID_clusters_list, group1_label = "COVID-19",
              clusters_list2 = HC_clusters_list, group2_label = "HC")
[1] "p-value: 1.1e-15"

## 3.3.2 Tcf20 score
# Tcf20 score represents the proportion of sequences attributed to the top 20 clonal families out of the total number of BCR sequences.
# Boxplot showing the Tcf20 scores between two groups.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
Tcf20.plot(clusters_list1 = COVID_clusters_list, group1_label = "COVID-19",
           clusters_list2 = HC_clusters_list, group2_label = "HC")
[1] "p-value: 0.0079"

## 3.4 Somatic hypermutation (SHM) analysis
# Calculate the average SHM ratio of all clusters in each sample.
# The calculation of SHM ratios may take a while.
SHM_df = SHM.calculation(clusters_list1 = COVID_clusters_list, raw_data_list1 = COVID_raw_data_list, group1_label = "COVID-19",
                         clusters_list2 = HC_clusters_list, raw_data_list2 = HC_raw_data_list, group2_label = "HC")
[1] "group1 processing:"

  |                                                                                                                                                                                               
  |                                                                                                                                                                                         |   0%
  |                                                                                                                                                                                               
  |=====================================                                                                                                                                                    |  20%
  |                                                                                                                                                                                               
  |==========================================================================                                                                                                               |  40%
  |                                                                                                                                                                                               
  |===============================================================================================================                                                                          |  60%
  |                                                                                                                                                                                               
  |====================================================================================================================================================                                     |  80%
  |                                                                                                                                                                                               
  |=========================================================================================================================================================================================| 100%[1] "group2 processing:"

  |                                                                                                                                                                                               
  |                                                                                                                                                                                         |   0%
  |                                                                                                                                                                                               
  |=====================================                                                                                                                                                    |  20%
  |                                                                                                                                                                                               
  |==========================================================================                                                                                                               |  40%
  |                                                                                                                                                                                               
  |===============================================================================================================                                                                          |  60%
  |                                                                                                                                                                                               
  |====================================================================================================================================================                                     |  80%
  |                                                                                                                                                                                               
  |=========================================================================================================================================================================================| 100%
# Boxplot showing the SHM ratios between two groups.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
SHM.plot(SHM_df = SHM_df)
[1] "p-value: 0.0079"

# Calculate the SHM ratios of clustered sequences in different isotypes in each sample.
# The calculation of SHM ratios may take a while.
SHM_iso_df = SHM.iso.calculation(clusters_list1 = COVID_clusters_list, raw_data_list1 = COVID_raw_data_list, group1_label = "COVID-19",
                                 clusters_list2 = HC_clusters_list, raw_data_list2 = HC_raw_data_list, group2_label = "HC")
[1] "group1 processing:"

  |                                                                                                                                                        
  |                                                                                                                                                  |   0%
  |                                                                                                                                                        
  |=============================                                                                                                                     |  20%
  |                                                                                                                                                        
  |==========================================================                                                                                        |  40%
  |                                                                                                                                                        
  |========================================================================================                                                          |  60%
  |                                                                                                                                                        
  |=====================================================================================================================                             |  80%
  |                                                                                                                                                        
  |==================================================================================================================================================| 100%[1] "group2 processing:"

  |                                                                                                                                                                  
  |                                                                                                                                                            |   0%
  |                                                                                                                                                                  
  |===============================                                                                                                                             |  20%
  |                                                                                                                                                                  
  |==============================================================                                                                                              |  40%
  |                                                                                                                                                                  
  |==============================================================================================                                                              |  60%
  |                                                                                                                                                                  
  |=============================================================================================================================                               |  80%
  |                                                                                                                                                                  
  |============================================================================================================================================================| 100%
# Boxplot showing the SHM ratios of clustered sequences in different isotypes between two groups.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
SHM.iso.plot(SHM_iso_df = SHM_iso_df)
 [1] "COVID-19_IGHD~COVID-19_IGHM p-value: 0.0079" "COVID-19_IGHD~COVID-19_IGHA p-value: 0.0079" "COVID-19_IGHD~COVID-19_IGHG p-value: 0.0079"
 [4] "COVID-19_IGHD~HC_IGHA p-value: 0.0159"       "COVID-19_IGHD~HC_IGHG p-value: 0.0079"       "COVID-19_IGHM~HC_IGHD p-value: 0.0079"      
 [7] "COVID-19_IGHM~HC_IGHM p-value: 0.0079"       "COVID-19_IGHM~HC_IGHA p-value: 0.0079"       "COVID-19_IGHM~HC_IGHG p-value: 0.0159"      
[10] "COVID-19_IGHA~HC_IGHD p-value: 0.0079"       "COVID-19_IGHA~HC_IGHM p-value: 0.0079"       "COVID-19_IGHA~HC_IGHA p-value: 0.0079"      
[13] "COVID-19_IGHA~HC_IGHG p-value: 0.0079"       "COVID-19_IGHG~HC_IGHD p-value: 0.0079"       "COVID-19_IGHG~HC_IGHM p-value: 0.0079"      
[16] "COVID-19_IGHG~HC_IGHA p-value: 0.0079"       "COVID-19_IGHG~HC_IGHG p-value: 0.0079"       "HC_IGHD~HC_IGHM p-value: 0.0159"            
[19] "HC_IGHD~HC_IGHA p-value: 0.0079"             "HC_IGHD~HC_IGHG p-value: 0.0079"             "HC_IGHM~HC_IGHA p-value: 0.0079"            
[22] "HC_IGHM~HC_IGHG p-value: 0.0079"            

### 3.5 NAb ratio calculation
## 3.5.1 Load the public antibody database to get the information of neutralizing antibody (NAb) sequences.
# Here is an example of the Coronavirus Antibody Database (CoV-AbDab).
CoV_AbDab <- read.csv("example/CoV-AbDab_130623.csv")

## 3.5.2 NAb ratio calculation
# NAb ratio is established as an indicator of the proportional prevalence of neutralizing antibody sequences within expanded clonalfamilies in each sample.
# It is defined as the fraction of the number of NAb sequences within clonal families to the total number of NAb sequences present in each sample.
# NAb sequences are queried using NAb.query() function where the parameter "method" represents the CDRH3 matching method.
# It can be "NA" for perfect match, "hamming" for hamming distance or "lv" for Levenshtein distance. Defaults to "NA".
# The parameter "species" can be "Mouse" or "Human". Defaults to "Human".
# The parameter "maxDist" represents the maximum distance allowed for matching when the argument "method" is "hamming" or "lv". Defaults to "NA".
NAb_ratio_df <- NAb.ratio.calculation(pro_data_list1 = COVID_pro_data_list, clusters_list1 = COVID_clusters_list, group1_label = "COVID-19",
                                      pro_data_list2 = HC_pro_data_list, clusters_list2 = HC_clusters_list, group2_label = "HC",
                                      AbDab = CoV_AbDab, method = NA, maxDist = NA, species = "Human")
[1] "group1 processing:"

  |                                                                                                                                                        
  |                                                                                                                                                  |   0%
  |                                                                                                                                                        
  |=============================                                                                                                                     |  20%
  |                                                                                                                                                        
  |==========================================================                                                                                        |  40%
  |                                                                                                                                                        
  |========================================================================================                                                          |  60%
  |                                                                                                                                                        
  |=====================================================================================================================                             |  80%
  |                                                                                                                                                        
  |==================================================================================================================================================| 100%[1] "group2 processing:"

  |                                                                                                                                                        
  |                                                                                                                                                  |   0%
  |                                                                                                                                                        
  |=============================                                                                                                                     |  20%
  |                                                                                                                                                        
  |==========================================================                                                                                        |  40%
  |                                                                                                                                                        
  |========================================================================================                                                          |  60%
  |                                                                                                                                                        
  |=====================================================================================================================                             |  80%
  |                                                                                                                                                        
  |==================================================================================================================================================| 100%
# Boxplot showing the NAb ratios between the two groups.
# Statistical comparisons are carried out by the two-sided Wilcoxon rank-sum test.
NAb.ratio.plot(NAb_ratio_df)
[1] "p-value: 0.025"

LS0tCnRpdGxlOiAiZmFzdEJDUiBwaXBlbGluZSBub3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBUUlVFfQojIFlvdSBzaG91bGQgZG93bmxvYWQgYW5kIHVuemlwICJmYXN0QkNSLW1haW4uemlwIiBmcm9tICJodHRwczovL2dpdGh1Yi5jb20vWmhhbmdMYWJUSlUvZmFzdEJDUiIgYW5kIHNldCBpdCBhcyB3b3JraW5nIGRpcmVjdG9yeSBiZWZvcmUgcnVubmluZyBwaXBlbGluZS4KIyBrbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICJUSEUvUEFUSC9UTy9GQVNUQkNSL0ZPTERFUiIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIn4vRG9jdW1lbnRzL1JwYWNrYWdlL2Zhc3RCQ1ItbWFpbiIpICMgUmVwbGFjZSB3aXRoIHlvdXIgcGF0aC4KYGBgCgpgYGB7cn0KbGlicmFyeShmYXN0QkNSKQpgYGAKCmBgYHtyfQojIyMgRmFzdCBCQ1IgY2xvbmFsIGZhbWlseSBpbmZlcmVuY2UKIyMgMS4gRGF0YSBsb2FkaW5nCiMgUGF0aCB0byB0aGUgIkNPVklEIi8iSEMiIGZvbGRlciBpbiB0aGUgImV4YW1wbGUiIGZvbGRlciBpbiBmYXN0QkNSIHByb2plY3QuCkNPVklEX2ZvbGRlcl9wYXRoIDwtICJleGFtcGxlL0NPVklEIgpIQ19mb2xkZXJfcGF0aCA8LSAiZXhhbXBsZS9IQyIKIyBMb2FkIGZpbGVzIGZyb20gIkNPVklEX2ZvbGRlcl9wYXRoIi8iSENfZm9sZGVyX3BhdGgiIGludG8gbGlzdC4KIyBUaGUgc3RvcmFnZSBmb3JtYXQgb2YgZGF0YSBjYW4gYmUgaW4gImNzdiIsICJ0c3YiLCBvciAiUmRhdGEiIGZvcm1hdC4KIyBUaGUgY29tcHJlc3NlZCBmaWxlcyBpbiB0aGUgYWJvdmUgc3RvcmFnZSBmb3JtYXQgaW4gIjd6aXAiLCAiY2FiIiwgImNwaW8iLCAiaXNvOTY2MCIsICJsaGEiLCAibXRyZWUiLCAic2hhciIsICJyYXIiLCAicmF3IiwgInRhciIsICJ4YXIiLCAiemlwIiwgIndhcmMiIGZvcm1hdCBjYW4gYWxzbyBiZSByZWFkIGluLgpDT1ZJRF9yYXdfZGF0YV9saXN0IDwtIGRhdGEubG9hZChmb2xkZXJfcGF0aCA9IENPVklEX2ZvbGRlcl9wYXRoLCBzdG9yYWdlX2Zvcm1hdCA9ICJjc3YiKQpIQ19yYXdfZGF0YV9saXN0IDwtIGRhdGEubG9hZChmb2xkZXJfcGF0aCA9IEhDX2ZvbGRlcl9wYXRoLCBzdG9yYWdlX2Zvcm1hdCA9ICJjc3YiKQpgYGAKCmBgYHtyfQojIyAyLiBEYXRhIHByZXByb2Nlc3NpbmcKIyBQcmVwcm9jZXNzaW5nIG9mIHJhdyBkYXRhIHRvIG1lZXQgdGhlIGlucHV0IHJlcXVpcmVtZW50cyBmb3IgY2xvbmFsIGZhbWlseSBpbmZlcmVuY2UuCiMgVGhlIGlucHV0IGRhdGEgbmVlZHMgdG8gY29udGFpbiBlc3NlbnRpYWwgY29sdW1ucyBpbmNsdWRpbmcgInZfY2FsbCIgKFYgZ2VuZSB3aXRoIG9yIHdpdGhvdXQgYWxsZWxlKSwgImpfY2FsbCIgKEogZ2VuZSB3aXRoIG9yIHdpdGhvdXQgYWxsZWxlKSBhbmQgImp1bmN0aW9uX2FhIiAoYW1pbm8gYWNpZCB0cmFuc2xhdGlvbiBvZiB0aGUganVuY3Rpb24pLiAKIyBUaGUgImp1bmN0aW9uIiAoanVuY3Rpb24gcmVnaW9uIG51Y2xlb3RpZGUgc2VxdWVuY2UsIHdoZXJlIHRoZSBqdW5jdGlvbiBpcyBkZWZpbmVkIGFzIHRoZSBDRFIzIHBsdXMgdGhlIHR3byBmbGFua2luZyBjb25zZXJ2ZWQgY29kb25zKSBjb2x1bW4gaXMgbmVlZGVkIGZvciBwaHlsb2dlbmV0aWMgdHJlZSBjb25zdHJ1Y3Rpb24gYW5kIFNITS1yZWxhdGVkIGFuYWx5c2lzLgojIFRoZSAiY19jYWxsIiAoY29uc3RhbnQgcmVnaW9uIGdlbmUgd2l0aCBvciB3aXRob3V0IGFsbGVsZSkgY29sdW1uIGlzIG5lZWRlZCBmb3IgaXNvdHlwZS1yZWxhdGVkIGFuYWx5c2lzLgojIER1cmluZyBkYXRhIHByZXByb2Nlc3NpbmcsIG9ubHkgcHJvZHVjdGl2ZSBzZXF1ZW5jZXMgd2hvc2UganVuY3Rpb24gYW1pbm8gYWNpZCBsZW5ndGhzIGJldHdlZW4gOSBhbmQgMjYgYXJlIHJlc2VydmVkLgojIFRoZSAiY291bnRfY29sX25hbWUiIHBhcmFtZXRlciByZXByZXNlbnRzIHRoZSBjb2x1bW4gbmFtZSBmb3IgdGhlIGNvdW50IG9mIGVhY2ggc2VxdWVuY2UuCiMgSXQgZGVmYXVsdHMgdG8gIk5BIiB3aGljaCBtZWFucyB0aGUgb3JpZ2luYWwgY291bnQgb2YgdGhlIHNlcXVlbmNlIGlzIG5vdCB0YWtlbiBpbnRvIGFjY291bnQuCiMgU2VxdWVuY2VzIHdpdGggdGhlIHNhbWUgInZfY2FsbCIsICJqX2NhbGwiIGFuZCAianVuY3Rpb25fYWEiIGFyZSBjb25zaWRlcmVkIHRvIGJlIHRoZSBzYW1lIGNsb25vdHlwZSBhbmQgYXJlIG1lcmdlZCBpbnRvIG9uZSByb3cgaW4gcHJvY2Vzc2VkIGRhdGEuCiMgVGhlIGNvbHVtbiAiY2xvbm90eXBlX2NvdW50IiBpcyB0aGUgbnVtYmVyIG9mIHNlcXVlbmNlcyBiZWxvbmdpbmcgdG8gZWFjaCBjbG9ub3R5cGUuCiMgVGhlIGNvbHVtbiAiY2xvbmVfY291bnQiIGlzIHRoZSBzdW0gb2YgdGhlIGNvdW50cyAoY2FsY3VsYXRlZCBiYXNlZCBvbiAiY291bnRfY29sX25hbWUiIHBhcmFtZXRlcikgb2YgdGhlIHNlcXVlbmNlcyBiZWxvbmdpbmcgdG8gZWFjaCBjbG9ub3R5cGUuCiMgVGhlIGNvbHVtbiAiY2xvbmVfZnJlIiBpcyB0aGUgZnJlcXVlbmN5IHZlcnNpb24gb2YgImNsb25lX2NvdW50Ii4KIyBUaGUgaW5mb3JtYXRpb24gb2YgdGhlIHNlcXVlbmNlIHdpdGggdGhlIGhpZ2hlc3QgY291bnQgaW4gZWFjaCBjbG9ub3R5cGUgaXMgcmV0YWluZWQuCiMgVGhlIGxpc3QgImluZGV4X21hdGNoIiBzYXZlcyB0aGUgb3JpZ2luYWwgaW5kZXhlcyBvZiBzZXF1ZW5jZXMgZm9yIGVhY2ggY2xvbm90eXBlLgpDT1ZJRF9wcm9fZGF0YV9saXN0IDwtIGRhdGEucHJlcHJvY2VzcyhkYXRhX2xpc3QgPSBDT1ZJRF9yYXdfZGF0YV9saXN0LCBjb3VudF9jb2xfbmFtZSA9ICJjb25zZW5zdXNfY291bnQiKQpIQ19wcm9fZGF0YV9saXN0IDwtIGRhdGEucHJlcHJvY2VzcyhkYXRhX2xpc3QgPSBIQ19yYXdfZGF0YV9saXN0LCBjb3VudF9jb2xfbmFtZSA9ICJjb25zZW5zdXNfY291bnQiKQpgYGAKCmBgYHtyfQojIyAzLiBCQ1IgY2xvbmFsIGZhbWlseSBpbmZlcmVuY2UKIyBGYXN0IGNsb25hbCBmYW1pbHkgaW5mZXJlbmNlIGZyb20gcHJlcHJvY2Vzc2VkIGRhdGEuCiMgVGhlICJjbHVzdGVyX3RocmUiIHBhcmFtZXRlciByZXByZXNlbnRzIG1pbmltYWwgY2x1c3RlcmluZyBjcml0ZXJpYSAobWluaW11bSBudW1iZXIgb2Ygc2VxdWVuY2VzIG5lZWRlZCB0byBmb3JtIGEgY2x1c3RlcikgYW5kIGRlZmF1bHRzIHRvIDMuCiMgRm9yIGhpZ2ggZWZmaWNpZW5jeSwgdGhlICJjbHVzdGVyX3RocmUiIGlzIGluY3JlYXNlZCBieSAxIGZvciBldmVyeSAxMDAsMDAwIGVudHJpZXMgb2YgaW5wdXQgZGF0YS4KIyBUaGUgIm92ZXJsYXBfdGhyZSIgcGFyYW1ldGVyIHJlcHJlc2VudHMgb3ZlcmxhcCBjb2VmZmljaWVudCB0aHJlc2hvbGQgZm9yIG1lcmdpbmcgdHdvIGNsdXN0ZXJzLCBzZWxlY3RhYmxlIHJhbmdlICgwLDEpIGFuZCBkZWZhdWx0cyB0byAwLjEuCiMgTG93ZXIgIm92ZXJsYXBfdGhyZSIgbWF5IGxlYWQgdG8gb3ZlcmNsdXN0ZXJpbmcgd2hpbGUgaGlnaGVyIHRocmVzaG9sZHMgbWF5IGxlYWQgdG8gdGhlIHNwbGl0IG9mIGNsb25hbCBmYW1pbGllcy4KIyBUaGUgImNvbnNlbnN1c190aHJlIiBwYXJhbWV0ZXIgcmVwcmVzZW50cyB0aGUgY29uc2Vuc3VzIHNjb3JlIHRocmVzaG9sZCBmb3IgZmlsdGVyaW5nIGNhbmRpZGF0ZXMgYW5kIGRlZmF1bHRzIHRvIDAuOC4KIyBBIGhpZ2hlciAiY29uc2Vuc3VzX3RocmUiIG1lYW5zIHN0cmljdGVyIGluZmVyZW5jZSBvZiB0aGUgY2x1c3Rlci4KQ09WSURfY2x1c3RlcnNfbGlzdCA8LSBkYXRhLkJDUi5jbHVzdGVycyhwcm9fZGF0YV9saXN0ID0gQ09WSURfcHJvX2RhdGFfbGlzdCwgY2x1c3Rlcl90aHJlID0gMywgb3ZlcmxhcF90aHJlID0gMC4xLCBjb25zZW5zdXNfdGhyZSA9IDAuOCkKSENfY2x1c3RlcnNfbGlzdCA8LSBkYXRhLkJDUi5jbHVzdGVycyhwcm9fZGF0YV9saXN0ID0gSENfcHJvX2RhdGFfbGlzdCwgY2x1c3Rlcl90aHJlID0gMywgb3ZlcmxhcF90aHJlID0gMC4xLCBjb25zZW5zdXNfdGhyZSA9IDAuOCkKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDV9CiMjIyBEb3duc3RyZWFtIGFuYWx5c2lzCiMjIDEuIFNpbmdsZSBjbHVzdGVyIGFuYWx5c2lzCiMjIDEuMSBDb25zZXJ2ZWQgbW90aWZzIGRpc3RyaWJ1dGlvbgojIFZpc3VhbGl6YXRpb24gb2YgbXVsdGlwbGUgc2VxdWVuY2UgYWxpZ25tZW50IChNU0EpIG9mIGp1bmN0aW9uIHNlcXVlbmNlcyB3aXRoaW4gYSBjbHVzdGVyLgojIFRoZSBwYXJhbWV0ZXIgImluZGV4IiBhbGxvd3MgeW91IHRvIGNob29zZSBhIGNsdXN0ZXIgZm9yIHZpc3VhbGl6YXRpb24uCiMgVGhlIHBhcmFtZXRlciAidHlwZSIgY2FuIGJlICJBQSIgZm9yIGFtaW5vIGFjaWQgb3IgIkROQSIgZm9yIGRlb3h5cmlib251Y2xlaWMgYWNpZC4KIyBUaGUgcGFyYW1ldGVyICJyYXdfZGF0YSIgaXMgdGhlIHJhdyBkYXRhIHdoaWNoIHRoZSBjbG9uYWwgZmFtaWxpZXMgaW5mZXJyZWQgZnJvbSBhbmQgZGVmYXVsdHMgdG8gIk5BIi4KIyBJdCBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gcGxvdCB0aGUgTVNBIG9mICdETkEnIHNlcXVlbmNlcy4KIyBmYXN0QkNSIHdpbGwgcmV0cmlldmUgYWxsIHRoZSBETkEgc2VxdWVuY2VzLCB3aGljaCBjYW4gYmUgbXVsdGlwbGUgc2VxdWVuY2VzIGR1ZSB0byB0aGUgZGVnZW5lcmFjeSBvZiBjb2RvbnMsIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgYW1pbm8gYWNpZCBzZXF1ZW5jZSBvZiBlYWNoIGNsb25vdHlwZSBmcm9tIHRoZSByYXcgZGF0YQpDT1ZJRF8wMV9jbHVzdGVycyA8LSBDT1ZJRF9jbHVzdGVyc19saXN0JENPVklEXzAxCkhDXzAxX2NsdXN0ZXJzIDwtIEhDX2NsdXN0ZXJzX2xpc3QkSENfMDEKbXNhLnBsb3QoYmNyX2NsdXN0ZXJzID0gQ09WSURfMDFfY2x1c3RlcnMsIGluZGV4ID0gMjAwLCB0eXBlID0gIkFBIikKbXNhLnBsb3QoYmNyX2NsdXN0ZXJzID0gQ09WSURfMDFfY2x1c3RlcnMsIGluZGV4ID0gMjAwLCB0eXBlID0gIkROQSIsIHJhd19kYXRhID0gQ09WSURfcmF3X2RhdGFfbGlzdCRDT1ZJRF8wMSkKYGBgCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMi41LCBmaWcud2lkdGggPSAxNH0KIyMgMS4yIFNlcXVlbmNlIGxvZ28KIyBWaXN1YWxpemF0aW9uIG9mIHNlcXVlbmNlIGxvZ28gb2YganVuY3Rpb24gc2VxdWVuY2VzIHdpdGhpbiBhIGNsdXN0ZXIuCnNlcWxvZ28ucGxvdChiY3JfY2x1c3RlcnMgPSBDT1ZJRF8wMV9jbHVzdGVycywgaW5kZXggPSAyMDAsIHR5cGUgPSAiQUEiKQpzZXFsb2dvLnBsb3QoYmNyX2NsdXN0ZXJzID0gQ09WSURfMDFfY2x1c3RlcnMsIGluZGV4ID0gMjAwLCB0eXBlID0gIkROQSIsIHJhd19kYXRhID0gQ09WSURfcmF3X2RhdGFfbGlzdCRDT1ZJRF8wMSkKYGBgCgpgYGB7cn0KIyMgMS4zIEV2b2x1dGlvbmFyeSB0cmVlCiMgUmVjb25zdHJ1Y3QgYSBCIGNlbGwgbGluZWFnZSB0cmVlIHdpdGggbWluaW11bSBzcGFubmluZyB0cmVlIGFuZCBnZW5vdHlwZSBhYnVuZGFuY2VzIHVzaW5nIENsb25hbFRyZWUuCiMgVGhlIGp1bmN0aW9uIG9mIEJDUiBzZXF1ZW5jZXMgd2l0aGluIGEgY2x1c3RlciB3aWxsIGJlIHdyaXR0ZW4gYXMgIkNsb25hbEZhbWlseV9pbmRleC5mYXN0YSIgaW4gIkNsb25hbFRyZWUvRXhhbXBsZXMvaW5wdXQiIGZvbGRlci4KIyBDbG9uYWxUcmVlIHVzZXMgUHl0aG9uIGZvciBjb21waWxhdGlvbiwgc28gaXQgbmVlZHMgdGhlIGFic29sdXRlIHBhdGggb2YgdGhlIFB5dGhvbiBpbnRlcnByZXRlciBpbiB0aGUgInB5dGhvbl9wYXRoIiBwYXJhbWV0ZXIuCiMgQ2xvbmFsVHJlZSByZXR1cm5zIHR3byBmaWxlcyBpbiB0aGUgIkNsb25hbFRyZWUvRXhhbXBsZXMvb3V0cHV0IiBmb2xkZXIuCiMgQ2xvbmFsRmFtaWx5X2luZGV4Lm5rOiB0aGUgcmVjb25zdHJ1Y3RlZCBCQ1IgbGluZWFnZSB0cmVlIGluIG5ld2ljayBmb3JtYXQuCiMgQ2xvbmFsRmFtaWx5X2luZGV4Lm5rLmNzdjogYSB0YWJsZSBpbiBjc3YgZm9ybWF0LCBjb250YWluaW5nIHRoZSBwYXJlbnQgcmVsYXRpb25zaGlwIGFuZCBjb3N0LgpjbG9uYWwudHJlZS5nZW5lcmF0aW9uKGJjcl9jbHVzdGVycyA9IENPVklEXzAxX2NsdXN0ZXJzLCBpbmRleCA9IDIwMCwgcmF3X2RhdGEgPSBDT1ZJRF9yYXdfZGF0YV9saXN0JENPVklEXzAxLCBweXRob25fcGF0aCA9ICIvVXNlcnMvd2FuZ2thaXh1YW4vYW5hY29uZGEzL2Jpbi9weXRob24iKSAjIFJlcGxhY2Ugd2l0aCB5b3VyIHBhdGgKIyBZb3UgY2FuIHVzZSB0aGUgY2xvbmFsLnRyZWUucGxvdCgpIGZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSB0aGUgZXZvbHV0aW9uYXJ5IHRyZWUgaW4gUi4KIyBUaGUgY29uc2Vuc3VzIHNlcXVlbmNlIG9mIHRoZSBjbHVzdGVyIGlzIHVzZWQgYXMgdGhlIHJvb3Qgbm9kZSBvZiB0aGUgdHJlZS4KIyBPcmFuZ2UgcG9pbnRzIHJlcHJlc2VudCBub2RlcyBhbmQgYmx1ZSBwb2ludHMgcmVwcmVzZW50IHRpcHMuCiMgVGhlIHgtYXhpcyBzaG93cyB0aGUgYWJzb2x1dGUgZ2VuZXRpYyBkaXN0YW5jZS4KY2xvbmFsLnRyZWUucGxvdChua19wYXRoID0gIkNsb25hbFRyZWUvRXhhbXBsZXMvb3V0cHV0L0Nsb25hbEZhbWlseV8yMDAuYWJSVC5uayIpCmBgYApgYGB7ciwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDV9CiMjIDEuNCBDbGFzcyBzd2l0Y2ggcmVjb21iaW5hdGlvbiAoQ1NSKSBuZXR3b3JrCiMgVmlzdWFsaXphdGlvbiBvZiBpc290eXBlIGNvLW9jY3VycmVuY2Ugd2l0aGluIGNsdXN0ZXJzIHdpdGhpbiBhIGNsdXN0ZXIKIyBDaXJjbGUgc2l6ZSByZXByZXNlbnRzIHRoZSBudW1iZXIgb2Ygc2VxdWVuY2VzIGNhcnJ5aW5nIGEgZ2l2ZW4gaXNvdHlwZS4KIyBMaW5lcyBjb25uZWN0aW5nIHR3byBjaXJjbGVzIGluZGljYXRlIHRoZSBlbnJpY2htZW50IGxldmVsIG9mIG9ic2VydmluZyBzd2l0Y2hlcyBpbiB0aGUgdHdvIGNvcnJlc3BvbmRpbmcgaW1tdW5vZ2xvYnVsaW4gc3ViY2xhc3Nlcy4KIyBUaGUgZW5yaWNobWVudCBsZXZlbCBpcyB0aGUgcmF0aW8gb2Ygb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIHN3aXRjaGVzIGlmIGltbXVub2dsb2J1bGluIGlzb3R5cGVzIGFyZSBhc3N1bWVkIHRvIGJlIGluZGVwZW5kZW50bHkgZGlzdHJpYnV0ZWQgYW1vbmcgY2x1c3Rlci4KIyBNYXRyaXggb2YgdmFsdWVzIG9mIGNvbm5lY3RlZCBlZGdlcyBiZXR3ZWVuIGNsdXN0ZXJlZCBzZXF1ZW5jZXMgaW4gZGlmZmVyZW50IGlzb3R5cGVzIGlzIHByaW50ZWQuCkNTUi5jbHVzdGVyLnBsb3QoYmNyX2NsdXN0ZXJzID0gQ09WSURfMDFfY2x1c3RlcnMsIGluZGV4ID0gNTApCmBgYAoKYGBge3J9CiMjIDIuIFNpbmdsZSBzYW1wbGUvZ3JvdXAgYW5hbHlzaXMKIyMgMi4xIENsYXNzaWZpY2F0aW9uIG9mIGNsdXN0ZXJlZCBhbmQgdW5jbHVzdGVyZWQgc2VxdWVuY2VzCiMgTWVyZ2UgYWxsIHRoZSBjbHVzdGVyZWQgc2VxdWVuY2VzIGluIGVhY2ggc2FtcGxlIGludG8gImNsdXN0ZXJlZF9zZXFzIi4KIyBNZXJnZSBhbGwgdGhlIHVuY2x1c3RlcmVkIHNlcXVlbmNlcyBpbiBlYWNoIHNhbXBsZSBpbnRvICJ1bmNsdXN0ZXJlZF9zZXFzIi4KQ09WSURfc2Vxc19saXN0IDwtIENsdXN0ZXJlZC5zZXFzKHByb19kYXRhX2xpc3QgPSBDT1ZJRF9wcm9fZGF0YV9saXN0LCBjbHVzdGVyc19saXN0ID0gQ09WSURfY2x1c3RlcnNfbGlzdCkKSENfc2Vxc19saXN0IDwtIENsdXN0ZXJlZC5zZXFzKHByb19kYXRhX2xpc3QgPSBIQ19wcm9fZGF0YV9saXN0LCBjbHVzdGVyc19saXN0ID0gSENfY2x1c3RlcnNfbGlzdCkKYGBgCgpgYGB7cn0KIyMgMi4yIFN1bW1hcnkgb2YgY2x1c3RlcnMgZnJvbSBhIHNhbXBsZQojIFN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzLCB0aGUgYXZlcmFnZSBzaXplIG9mIGNsdXN0ZXJzIGFuZCB0aGUgcHJvcG9ydGlvbiBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzLgpDT1ZJRF9jbHVzdGVyc19zdW1tYXJ5IDwtIENsdXN0ZXJzLnN1bW1hcnkocHJvX2RhdGFfbGlzdCA9IENPVklEX3Byb19kYXRhX2xpc3QsIGNsdXN0ZXJzX2xpc3QgPSBDT1ZJRF9jbHVzdGVyc19saXN0KQpIQ19jbHVzdGVyc19zdW1tYXJ5IDwtIENsdXN0ZXJzLnN1bW1hcnkocHJvX2RhdGFfbGlzdCA9IEhDX3Byb19kYXRhX2xpc3QsIGNsdXN0ZXJzX2xpc3QgPSBIQ19jbHVzdGVyc19saXN0KQpDT1ZJRF8wMV9zdW1tYXJ5IDwtIENPVklEX2NsdXN0ZXJzX3N1bW1hcnkkQ09WSURfMDEKcHJpbnQoJ1N1bW1hcnkgb2YgQ09WSURfMDE6JykKcHJpbnQoQ09WSURfMDFfc3VtbWFyeSkKSENfMDFfc3VtbWFyeSA8LSBIQ19jbHVzdGVyc19zdW1tYXJ5JEhDXzAxCnByaW50KCdTdW1tYXJ5IG9mIEhDXzAxOicpCnByaW50KEhDXzAxX3N1bW1hcnkpCmBgYAoKYGBge3J9CiMjIDIuMyBWaXN1YWxpemF0aW9uIG9mIGNsdXN0ZXJzIGZyb20gYSBzYW1wbGUKIyBQb2ludCBkaWFncmFtIHNob3dpbmcgY2x1c3RlcnMgZnJvbSBhIHNhbXBsZSB3aGVyZSBhIGNpcmNsZSByZXByZXNlbnRzIGEgY2x1c3Rlci4KIyBUaGUgc2l6ZSBhbmQgY29sb3Igb2YgdGhlIGNpcmNsZSByZXByZXNlbnRzIHRoZSBzaXplIG9mIHRoZSBjbHVzdGVyLgojIFRoZSBwYXJhbWV0ZXIgImluZGV4IiByZXByZXNlbnRzIHRoZSBpbmRleCBvZiB0aGUgc2FtcGxlIGZvciB2aXN1YWxpemF0aW9uLgpDbHVzdGVycy52aXN1YWxpemF0aW9uKHByb19kYXRhX2xpc3QgPSBDT1ZJRF9wcm9fZGF0YV9saXN0LCBjbHVzdGVyc19saXN0ID0gQ09WSURfY2x1c3RlcnNfbGlzdCwgaW5kZXggPSAxKQpDbHVzdGVycy52aXN1YWxpemF0aW9uKHByb19kYXRhX2xpc3QgPSBIQ19wcm9fZGF0YV9saXN0LCBjbHVzdGVyc19saXN0ID0gSENfY2x1c3RlcnNfbGlzdCwgaW5kZXggPSAxKQpgYGAKCmBgYHtyfQojIyAyLjQgVi9KIGdlbmUgdXNhZ2UKIyMgMi40LjEgUGllIGNoYXJ0OiBWL0ogZ2VuZQojIFBpZSBjaGFydCBzaG93aW5nIHRoZSBnZW5lIHVzYWdlIGZyZXF1ZW5jeSBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzLgojIFRoZSB0b3AgdGVuIG1vc3QgZnJlcXVlbnQgZ2VuZXMgYXJlIHNob3duLCBhbmQgdGhlIHJlc3QgYXJlIHJlcHJlc2VudGVkIGJ5ICJvdGhlcnMiLgojIFRoZSBwYXJhbWV0ZXIgImNvbG5hbWUiIGNhbiBiZSAidl9jYWxsIiBmb3IgViBnZW5lIG9yICJqX2NhbGwiIGZvciBKIGdlbmUuCiMgKDEpIHNpbmdsZSBzYW1wbGUKQ09WSURfMDFfY2x1c3RlcmVkX3NlcXMgPC0gQ09WSURfc2Vxc19saXN0JGNsdXN0ZXJlZF9zZXFzJENPVklEXzAxCkNPVklEXzAxX3VuY2x1c3RlcmVkX3NlcXMgPC0gQ09WSURfc2Vxc19saXN0JHVuY2x1c3RlcmVkX3NlcXMkQ09WSURfMDEKSENfMDFfY2x1c3RlcmVkX3NlcXMgPC0gSENfc2Vxc19saXN0JGNsdXN0ZXJlZF9zZXFzJEhDXzAxCnBpZS5mcmVxLnBsb3QoY2x1c3RlcmVkX3NlcXMgPSBDT1ZJRF8wMV9jbHVzdGVyZWRfc2VxcywgY29sbmFtZSA9ICJ2X2NhbGwiKQpwaWUuZnJlcS5wbG90KGNsdXN0ZXJlZF9zZXFzID0gSENfMDFfY2x1c3RlcmVkX3NlcXMsIGNvbG5hbWUgPSAidl9jYWxsIikKIyAoMikgbXVsdGlwbGUgc2FtcGxlcyBpbiBhIGdyb3VwLgojIEFsbCB0aGUgY2x1c3RlcmVkL3VuY2x1c3RlcmVkIHNlcXVlbmNlcyBmcm9tIG11bHRpcGxlIHNhbXBsZXMgaW4gYSBncm91cCBjYW4gYmUgbWVyZ2VkLgpDT1ZJRF9hbGxfY2x1c3RlcmVkX3NlcXMgPC0gTlVMTApmb3IoaSBpbiAxOmxlbmd0aChDT1ZJRF9zZXFzX2xpc3QkY2x1c3RlcmVkX3NlcXMpKXsKICBDT1ZJRF9hbGxfY2x1c3RlcmVkX3NlcXMgPC0gcmJpbmQoQ09WSURfYWxsX2NsdXN0ZXJlZF9zZXFzLCBDT1ZJRF9zZXFzX2xpc3QkY2x1c3RlcmVkX3NlcXNbW2ldXSkKfQpDT1ZJRF9hbGxfdW5jbHVzdGVyZWRfc2VxcyA8LSBOVUxMCmZvcihpIGluIDE6bGVuZ3RoKENPVklEX3NlcXNfbGlzdCR1bmNsdXN0ZXJlZF9zZXFzKSl7CiAgQ09WSURfYWxsX3VuY2x1c3RlcmVkX3NlcXMgPC0gcmJpbmQoQ09WSURfYWxsX3VuY2x1c3RlcmVkX3NlcXMsIENPVklEX3NlcXNfbGlzdCR1bmNsdXN0ZXJlZF9zZXFzW1tpXV0pCn0KSENfYWxsX2NsdXN0ZXJlZF9zZXFzIDwtIE5VTEwKZm9yKGkgaW4gMTpsZW5ndGgoSENfc2Vxc19saXN0JGNsdXN0ZXJlZF9zZXFzKSl7CiAgSENfYWxsX2NsdXN0ZXJlZF9zZXFzIDwtIHJiaW5kKEhDX2FsbF9jbHVzdGVyZWRfc2VxcywgSENfc2Vxc19saXN0JGNsdXN0ZXJlZF9zZXFzW1tpXV0pCn0KSENfYWxsX3VuY2x1c3RlcmVkX3NlcXMgPC0gTlVMTApmb3IoaSBpbiAxOmxlbmd0aChIQ19zZXFzX2xpc3QkdW5jbHVzdGVyZWRfc2VxcykpewogIEhDX2FsbF91bmNsdXN0ZXJlZF9zZXFzIDwtIHJiaW5kKEhDX2FsbF91bmNsdXN0ZXJlZF9zZXFzLCBIQ19zZXFzX2xpc3QkdW5jbHVzdGVyZWRfc2Vxc1tbaV1dKQp9CnBpZS5mcmVxLnBsb3QoY2x1c3RlcmVkX3NlcXMgPSBDT1ZJRF9hbGxfY2x1c3RlcmVkX3NlcXMsIGNvbG5hbWUgPSAidl9jYWxsIikKcGllLmZyZXEucGxvdChjbHVzdGVyZWRfc2VxcyA9IEhDX2FsbF9jbHVzdGVyZWRfc2VxcywgY29sbmFtZSA9ICJ2X2NhbGwiKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQgPSA4LCBmaWcud2lkdGggPSAzLjJ9CiMjIDIuNC4yIEhlYXRtYXA6IFYtSiBnZW5lIHBhaXIKIyBIZWF0bWFwIHNob3dpbmcgdGhlIFYtSiBnZW5lIHBhaXIgZnJlcXVlbmN5IG9mIGNsdXN0ZXJlZCBzZXF1ZW5jZXMKIyAoMSkgc2luZ2xlIHNhbXBsZS4KdmpwYWlyLnNhbXBsZS5wbG90KGNsdXN0ZXJlZF9zZXFzID0gQ09WSURfMDFfY2x1c3RlcmVkX3NlcXMpCiMgKDIpIG11bHRpcGxlIHNhbXBsZXMgaW4gYSBncm91cC4KdmpwYWlyLnNhbXBsZS5wbG90KGNsdXN0ZXJlZF9zZXFzID0gQ09WSURfYWxsX2NsdXN0ZXJlZF9zZXFzKQp2anBhaXIuc2FtcGxlLnBsb3QoY2x1c3RlcmVkX3NlcXMgPSBIQ19hbGxfY2x1c3RlcmVkX3NlcXMpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQgPSAyLCBmaWcud2lkdGggPSA0fQojIyAyLjUgSnVuY3Rpb24gbGVuZ3RoIGRpc3RyaWJ1dGlvbgojIDIuNS4xIEhpc3RvZ3JhbSBhbmQgZGVuc2l0eSBwbG90IHNob3dpbmcgdGhlIGxlbmd0aCBkaXN0cmlidXRpb24gb2YganVuY3Rpb24gYW1pbm8gYWNpZCBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzLgojICgxKSBzaW5nbGUgc2FtcGxlCmxlbi5zYW1wbGUucGxvdChjbHVzdGVyZWRfc2VxcyA9IENPVklEXzAxX2NsdXN0ZXJlZF9zZXFzKQojICgyKSBtdWx0aXBsZSBzYW1wbGVzIGluIGEgZ3JvdXAKbGVuLnNhbXBsZS5wbG90KGNsdXN0ZXJlZF9zZXFzID0gQ09WSURfYWxsX2NsdXN0ZXJlZF9zZXFzKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQgPSAyLCBmaWcud2lkdGggPSAzLjV9CiMgMi41LjIgRGVuc2l0eSByaWRnZXMgc2hvd2luZyB0aGUgbGVuZ3RoIGRpc3RyaWJ1dGlvbnMgb2YganVuY3Rpb24gYW1pbm8gYWNpZCBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzIGFuZCB1bmNsdXN0ZXJlZCBzZXF1ZW5jZXMuCiMgU3RhdGlzdGljYWwgY29tcGFyaXNvbnMgYXJlIGNhcnJpZWQgb3V0IGJ5IHRoZSB0d28tc2lkZWQgV2lsY294b24gcmFuay1zdW0gdGVzdC4KIyAoMSkgc2luZ2xlIHNhbXBsZQpsZW4uY2x1c3RlcmVkLnBsb3QoY2x1c3RlcmVkX3NlcXMgPSBDT1ZJRF8wMV9jbHVzdGVyZWRfc2VxcywKICAgICAgICAgICAgICAgICAgIHVuY2x1c3RlcmVkX3NlcXMgPSBDT1ZJRF8wMV91bmNsdXN0ZXJlZF9zZXFzKQojICgyKSBtdWx0aXBsZSBzYW1wbGVzIGluIGEgZ3JvdXAKbGVuLmNsdXN0ZXJlZC5wbG90KGNsdXN0ZXJlZF9zZXFzID0gQ09WSURfYWxsX2NsdXN0ZXJlZF9zZXFzLAogICAgICAgICAgICAgICAgICAgdW5jbHVzdGVyZWRfc2VxcyA9IENPVklEX2FsbF91bmNsdXN0ZXJlZF9zZXFzKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gNX0KIyMgMi42IENsYXNzIHN3aXRjaCByZWNvbWJpbmF0aW9uIChDU1IpIG5ldHdvcmsKIyBWaXN1YWxpemF0aW9uIG9mIGlzb3R5cGUgY28tb2NjdXJyZW5jZSB3aXRoaW4gY2x1c3RlcnMgZnJvbSBhIHNhbXBsZS4KIyBDaXJjbGUgc2l6ZSByZXByZXNlbnRzIHRoZSBudW1iZXIgb2Ygc2VxdWVuY2VzIGNhcnJ5aW5nIGEgZ2l2ZW4gaXNvdHlwZS4KIyBMaW5lcyBjb25uZWN0aW5nIHR3byBjaXJjbGVzIGluZGljYXRlIHRoZSBlbnJpY2htZW50IGxldmVsIG9mIG9ic2VydmluZyBzd2l0Y2hlcyBpbiB0aGUgdHdvIGNvcnJlc3BvbmRpbmcgaW1tdW5vZ2xvYnVsaW4gc3ViY2xhc3Nlcy4KIyBUaGUgZW5yaWNobWVudCBsZXZlbCBpcyB0aGUgcmF0aW8gb2Ygb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIHN3aXRjaGVzIGlmIGltbXVub2dsb2J1bGluIGlzb3R5cGVzIGFyZSBhc3N1bWVkIHRvIGJlIGluZGVwZW5kZW50bHkgZGlzdHJpYnV0ZWQgYW1vbmcgY2x1c3Rlci4KIyBNYXRyaXggb2YgdmFsdWVzIG9mIGNvbm5lY3RlZCBlZGdlcyBiZXR3ZWVuIGNsdXN0ZXJlZCBzZXF1ZW5jZXMgaW4gZGlmZmVyZW50IGlzb3R5cGVzIGlzIHByaW50ZWQuCkNTUi5zYW1wbGUucGxvdChiY3JfY2x1c3RlcnMgPSBDT1ZJRF8wMV9jbHVzdGVycykKQ1NSLnNhbXBsZS5wbG90KGJjcl9jbHVzdGVycyA9IEhDXzAxX2NsdXN0ZXJzKQpgYGAKCmBgYHtyfQojIyMgMi43IE5ldXRyYWxpemluZyBhbnRpYm9keSAoTkFiKSBzZXF1ZW5jZSBxdWVyeQojIyAyLjcuMSBMb2FkIHRoZSBwdWJsaWMgYW50aWJvZHkgZGF0YWJhc2UgdG8gZ2V0IHRoZSBpbmZvcm1hdGlvbiBvZiBuZXV0cmFsaXppbmcgYW50aWJvZHkgKE5BYikgc2VxdWVuY2VzLgojIEhlcmUgaXMgYW4gZXhhbXBsZSBvZiB0aGUgQ29yb25hdmlydXMgQW50aWJvZHkgRGF0YWJhc2UgKENvVi1BYkRhYikuCkNvVl9BYkRhYiA8LSByZWFkLmNzdigiZXhhbXBsZS9Db1YtQWJEYWJfMTMwNjIzLmNzdiIpCiMjIDIuNy4yIE5BYiBxdWVyeQojIFdlIGRlZmluZSB0aGF0IGlmIGEgc2VxdWVuY2UgaW4gYSBjbHVzdGVyIHNoYXJlcyB0aGUgc2FtZSBWIGFuZCBKIGdlbmVzIHdpdGggYW55IHNlcXVlbmNlIGluIHRoZSBwdWJsaWMgYW50aWJvZHkgbGlicmFyeSBhbmQgdGhlaXIgQ0RSSDMgbWF0Y2hlcywgdGhlbiB0aGlzIHNlcXVlbmNlIGlzIGtub3duIGFzIGEgTkFiIHNlcXVlbmNlLiAKIyBUaGUgcGFyYW1ldGVyICJtZXRob2QiIHJlcHJlc2VudHMgdGhlIENEUkgzIG1hdGNoaW5nIG1ldGhvZC4KIyBJdCBjYW4gYmUgIk5BIiBmb3IgcGVyZmVjdCBtYXRjaCwgImhhbW1pbmciIGZvciBoYW1taW5nIGRpc3RhbmNlIG9yICJsdiIgZm9yIExldmVuc2h0ZWluIGRpc3RhbmNlLiBEZWZhdWx0cyB0byAiTkEiLgojIFRoZSBwYXJhbWV0ZXIgInNwZWNpZXMiIGNhbiBiZSAiTW91c2UiIG9yICJIdW1hbiIuIERlZmF1bHRzIHRvICJIdW1hbiIuCiMgVGhlIHBhcmFtZXRlciAibWF4RGlzdCIgcmVwcmVzZW50cyB0aGUgbWF4aW11bSBkaXN0YW5jZSBhbGxvd2VkIGZvciBtYXRjaGluZyB3aGVuIHRoZSBhcmd1bWVudCAibWV0aG9kIiBpcyAiaGFtbWluZyIgb3IgImx2Ii4gRGVmYXVsdHMgdG8gIk5BIi4KIyBleGFtcGxlIHRvIHBlcmZlY3QgbWF0Y2gKaHVtYW5fcGVyZmVjdF9tYXRjaCA8LSBOQWIucXVlcnkoYmNyX2NsdXN0ZXJzID0gQ09WSURfMDFfY2x1c3RlcnMsIEFiRGFiID0gQ29WX0FiRGFiLCBtZXRob2QgPSBOQSwgbWF4RGlzdCA9IE5BLCBzcGVjaWVzID0gIkh1bWFuIikKaGVhZChodW1hbl9wZXJmZWN0X21hdGNoKQojIGV4YW1wbGUgd2l0aCBwZXJmZWN0IG1hdGNoIGluICJNb3VzZSIgc3BlY2llcwptb3VzZV9wZXJmZWN0X21hdGNoIDwtIE5BYi5xdWVyeShiY3JfY2x1c3RlcnMgPSBDT1ZJRF8wMV9jbHVzdGVycywgQWJEYWIgPSBDb1ZfQWJEYWIsIG1ldGhvZCA9IE5BLCBtYXhEaXN0ID0gTkEsIHNwZWNpZXMgPSAiTW91c2UiKQojIGV4YW1wbGUgd2l0aCBmdXp6eSBtYXRjaGluZyB3aXRoICJoYW1taW5nIiBtZXRob2QgYW5kIG1heCBkaXN0YW5jZSBvZiAxCmh1bWFuX2hhbW1pbmdfMV9tYXRjaCA8LSBOQWIucXVlcnkoYmNyX2NsdXN0ZXJzID0gQ09WSURfMDFfY2x1c3RlcnMsIEFiRGFiID0gQ29WX0FiRGFiLCBtZXRob2QgPSAiaGFtbWluZyIsIG1heERpc3QgPSAxLCBzcGVjaWVzID0gIkh1bWFuIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDIuNSwgZmlnLndpZHRoID0gMTJ9CiMjIDMuIEludGVyIGdyb3VwIGFuYWx5c2lzCiMjIDMuMSBWL0ogZ2VuZSB1c2FnZQojIyAzLjEuMSBCb3hwbG90OiBWL0ogZ2VuZQojIEJveHBsb3Qgc2hvd2luZyB0aGUgVi9KIGdlbmUgdXNhZ2Ugb2YgdGhlIGNsdXN0ZXJlZCBzZXF1ZW5jZXMgYmV0d2VlbiB0d28gZ3JvdXBzLgojIFRoZSBwYXJhbWV0ZXIgImNvbG5hbWUiIGNhbiBiZSAidl9jYWxsIiBmb3IgViBnZW5lIG9yICJqX2NhbGwiIGZvciBKIGdlbmUuCiMgU3RhdGlzdGljYWwgY29tcGFyaXNvbnMgYXJlIGNhcnJpZWQgb3V0IGJ5IHRoZSB0d28tc2lkZWQgV2lsY294b24gcmFuay1zdW0gdGVzdC4KZ2VuZS5mcmUucGxvdChncm91cDFfc2Vxc19saXN0ID0gQ09WSURfc2Vxc19saXN0LAogICAgICAgICAgICAgIGdyb3VwMV9hbGxfY2x1c3RlcmVkX3NlcXMgPSBDT1ZJRF9hbGxfY2x1c3RlcmVkX3NlcXMsCiAgICAgICAgICAgICAgZ3JvdXAxX2xhYmVsID0gIkNPVklELTE5IiwKICAgICAgICAgICAgICBncm91cDJfc2Vxc19saXN0ID0gSENfc2Vxc19saXN0LAogICAgICAgICAgICAgIGdyb3VwMl9hbGxfY2x1c3RlcmVkX3NlcXMgPSBIQ19hbGxfY2x1c3RlcmVkX3NlcXMsCiAgICAgICAgICAgICAgZ3JvdXAyX2xhYmVsID0gIkhDIiwKICAgICAgICAgICAgICBjb2xuYW1lID0gInZfY2FsbCIpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQgPSAyLjMsIGZpZy53aWR0aCA9IDMuNH0KZ2VuZS5mcmUucGxvdChncm91cDFfc2Vxc19saXN0ID0gQ09WSURfc2Vxc19saXN0LAogICAgICAgICAgICAgIGdyb3VwMV9hbGxfY2x1c3RlcmVkX3NlcXMgPSBDT1ZJRF9hbGxfY2x1c3RlcmVkX3NlcXMsCiAgICAgICAgICAgICAgZ3JvdXAxX2xhYmVsID0gIkNPVklELTE5IiwKICAgICAgICAgICAgICBncm91cDJfc2Vxc19saXN0ID0gSENfc2Vxc19saXN0LAogICAgICAgICAgICAgIGdyb3VwMl9hbGxfY2x1c3RlcmVkX3NlcXMgPSBIQ19hbGxfY2x1c3RlcmVkX3NlcXMsCiAgICAgICAgICAgICAgZ3JvdXAyX2xhYmVsID0gIkhDIiwKICAgICAgICAgICAgICBjb2xuYW1lID0gImpfY2FsbCIpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQgPSAzLjcsIGZpZy53aWR0aCA9IDE2fQojIyAzLjEuMiBIZWF0bWFwOiBWLUogZ2VuZSBwYWlyCiMgSGVhdG1hcCBzaG93aW5nIHRoZSBmb2xkIGNoYW5nZSBvZiBWLUogZ2VuZSBwYWlyIGZyZXF1ZW5jeSBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzIGJldHdlZW4gdHdvIGdyb3Vwcy4KIyBMb2cgZm9sZCBjaGFuZ2UgKGxvZyBGQykgaXMgY2FsY3VsYXRlZCBhcyB0aGUgbG9nMiByYXRpbyBvZiB0aGUgYXZlcmFnZSB2YWx1ZXMgYmV0d2VlbiBncm91cDEgYW5kIGdyb3VwMiBzYW1wbGVzLgojIFN0YXRpc3RpY2FsIGNvbXBhcmlzb25zIGFyZSBjYXJyaWVkIG91dCBieSB0aGUgdHdvLXNpZGVkIFdpbGNveG9uIHJhbmstc3VtIHRlc3QuCiMgRkRSIGNvcnJlY3Rpb24gd2FzIHBlcmZvcm1lZCB3aXRoIHRoZSBCZW5qYW1pbmnigJNIb2NoYmVyZyBwcm9jZWR1cmUuCiMgVGhlIFYtSiBnZW5lIHBhaXIgZnJlcXVlbmN5IG9mIHNhbXBsZXMgd2l0aCBhIGZyZXF1ZW5jeSBsZXNzIHRoYW4gdGhlIG1pbmltdW0gdmFsdWUgKDHigLApIGlzIHNldCB0byB0aGUgbWluaW11bSB2YWx1ZS4KdmpwYWlyLmdyb3VwLnBsb3QoZ3JvdXAxX3NlcXNfbGlzdCA9IENPVklEX3NlcXNfbGlzdCwKICAgICAgICAgICAgICAgICAgZ3JvdXAxX2FsbF9jbHVzdGVyZWRfc2VxcyA9IENPVklEX2FsbF9jbHVzdGVyZWRfc2VxcywKICAgICAgICAgICAgICAgICAgZ3JvdXAxX2xhYmVsID0gIkNPVklELTE5IiwKICAgICAgICAgICAgICAgICAgZ3JvdXAyX3NlcXNfbGlzdCA9IEhDX3NlcXNfbGlzdCwKICAgICAgICAgICAgICAgICAgZ3JvdXAyX2FsbF9jbHVzdGVyZWRfc2VxcyA9IEhDX2FsbF9jbHVzdGVyZWRfc2VxcywKICAgICAgICAgICAgICAgICAgZ3JvdXAyX2xhYmVsID0gIkhDIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDIsIGZpZy53aWR0aCA9IDMuNX0KIyMgMy4yIEp1bmN0aW9uIGxlbmd0aAojIERlbnNpdHkgcmlkZ2VzIHNob3dpbmcgdGhlIGxlbmd0aCBkaXN0cmlidXRpb24gb2YganVuY3Rpb24gYW1pbm8gYWNpZCBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzIGJldHdlZW4gdHdvIGdyb3Vwcy4KIyBTdGF0aXN0aWNhbCBjb21wYXJpc29ucyBhcmUgY2FycmllZCBvdXQgYnkgdGhlIHR3by1zaWRlZCBXaWxjb3hvbiByYW5rLXN1bSB0ZXN0LgpsZW4uZ3JvdXAucGxvdChncm91cDFfYWxsX2NsdXN0ZXJlZF9zZXFzID0gQ09WSURfYWxsX2NsdXN0ZXJlZF9zZXFzLCBncm91cDFfbGFiZWwgPSAiQ09WSUQtMTkiLAogICAgICAgICAgICAgICBncm91cDJfYWxsX2NsdXN0ZXJlZF9zZXFzID0gSENfYWxsX2NsdXN0ZXJlZF9zZXFzLCBncm91cDJfbGFiZWwgPSAiSEMiKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMywgZmlnLndpZHRoID0gNX0KIyMgMy4zIERpdmVyc2l0eSBhbmFseXNpcwojIyAzLjMuMSBOdW1iZXIgYW5kIHNpemUgb2YgY2x1c3RlcnMKIyBCdWJibGUgcGxvdCBzaG93aW5nIHRoZSBzaXplIGFuZCBudW1iZXIgb2YgY2x1c3RlcnMgYmV0d2VlbiB0d28gZ3JvdXBzLgpjbHUuc2l6ZS5wbG90KGNsdXN0ZXJzX2xpc3QxID0gQ09WSURfY2x1c3RlcnNfbGlzdCwgZ3JvdXAxX2xhYmVsID0gIkNPVklELTE5IiwKICAgICAgICAgICAgICBjbHVzdGVyc19saXN0MiA9IEhDX2NsdXN0ZXJzX2xpc3QsIGdyb3VwMl9sYWJlbCA9ICJIQyIpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQgPSAyLCBmaWcud2lkdGggPSAyLjJ9CiMjIDMuMy4yIFRjZjIwIHNjb3JlCiMgVGNmMjAgc2NvcmUgcmVwcmVzZW50cyB0aGUgcHJvcG9ydGlvbiBvZiBzZXF1ZW5jZXMgYXR0cmlidXRlZCB0byB0aGUgdG9wIDIwIGNsb25hbCBmYW1pbGllcyBvdXQgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBCQ1Igc2VxdWVuY2VzLgojIEJveHBsb3Qgc2hvd2luZyB0aGUgVGNmMjAgc2NvcmVzIGJldHdlZW4gdHdvIGdyb3Vwcy4KIyBTdGF0aXN0aWNhbCBjb21wYXJpc29ucyBhcmUgY2FycmllZCBvdXQgYnkgdGhlIHR3by1zaWRlZCBXaWxjb3hvbiByYW5rLXN1bSB0ZXN0LgpUY2YyMC5wbG90KGNsdXN0ZXJzX2xpc3QxID0gQ09WSURfY2x1c3RlcnNfbGlzdCwgZ3JvdXAxX2xhYmVsID0gIkNPVklELTE5IiwKICAgICAgICAgICBjbHVzdGVyc19saXN0MiA9IEhDX2NsdXN0ZXJzX2xpc3QsIGdyb3VwMl9sYWJlbCA9ICJIQyIpCmBgYApgYGB7cn0KIyMgMy40IFNvbWF0aWMgaHlwZXJtdXRhdGlvbiAoU0hNKSBhbmFseXNpcwojIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBTSE0gcmF0aW8gb2YgYWxsIGNsdXN0ZXJzIGluIGVhY2ggc2FtcGxlLgojIFRoZSBjYWxjdWxhdGlvbiBvZiBTSE0gcmF0aW9zIG1heSB0YWtlIGEgd2hpbGUuClNITV9kZiA9IFNITS5jYWxjdWxhdGlvbihjbHVzdGVyc19saXN0MSA9IENPVklEX2NsdXN0ZXJzX2xpc3QsIHJhd19kYXRhX2xpc3QxID0gQ09WSURfcmF3X2RhdGFfbGlzdCwgZ3JvdXAxX2xhYmVsID0gIkNPVklELTE5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJzX2xpc3QyID0gSENfY2x1c3RlcnNfbGlzdCwgcmF3X2RhdGFfbGlzdDIgPSBIQ19yYXdfZGF0YV9saXN0LCBncm91cDJfbGFiZWwgPSAiSEMiKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodCA9IDIsIGZpZy53aWR0aCA9IDIuMn0KIyBCb3hwbG90IHNob3dpbmcgdGhlIFNITSByYXRpb3MgYmV0d2VlbiB0d28gZ3JvdXBzLgojIFN0YXRpc3RpY2FsIGNvbXBhcmlzb25zIGFyZSBjYXJyaWVkIG91dCBieSB0aGUgdHdvLXNpZGVkIFdpbGNveG9uIHJhbmstc3VtIHRlc3QuClNITS5wbG90KFNITV9kZiA9IFNITV9kZikKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGUgdGhlIFNITSByYXRpb3Mgb2YgY2x1c3RlcmVkIHNlcXVlbmNlcyBpbiBkaWZmZXJlbnQgaXNvdHlwZXMgaW4gZWFjaCBzYW1wbGUuCiMgVGhlIGNhbGN1bGF0aW9uIG9mIFNITSByYXRpb3MgbWF5IHRha2UgYSB3aGlsZS4KU0hNX2lzb19kZiA9IFNITS5pc28uY2FsY3VsYXRpb24oY2x1c3RlcnNfbGlzdDEgPSBDT1ZJRF9jbHVzdGVyc19saXN0LCByYXdfZGF0YV9saXN0MSA9IENPVklEX3Jhd19kYXRhX2xpc3QsIGdyb3VwMV9sYWJlbCA9ICJDT1ZJRC0xOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJzX2xpc3QyID0gSENfY2x1c3RlcnNfbGlzdCwgcmF3X2RhdGFfbGlzdDIgPSBIQ19yYXdfZGF0YV9saXN0LCBncm91cDJfbGFiZWwgPSAiSEMiKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMi41LCBmaWcud2lkdGggPSA1fQojIEJveHBsb3Qgc2hvd2luZyB0aGUgU0hNIHJhdGlvcyBvZiBjbHVzdGVyZWQgc2VxdWVuY2VzIGluIGRpZmZlcmVudCBpc290eXBlcyBiZXR3ZWVuIHR3byBncm91cHMuCiMgU3RhdGlzdGljYWwgY29tcGFyaXNvbnMgYXJlIGNhcnJpZWQgb3V0IGJ5IHRoZSB0d28tc2lkZWQgV2lsY294b24gcmFuay1zdW0gdGVzdC4KU0hNLmlzby5wbG90KFNITV9pc29fZGYgPSBTSE1faXNvX2RmKQpgYGAKCmBgYHtyfQojIyMgMy41IE5BYiByYXRpbyBjYWxjdWxhdGlvbgojIyAzLjUuMSBMb2FkIHRoZSBwdWJsaWMgYW50aWJvZHkgZGF0YWJhc2UgdG8gZ2V0IHRoZSBpbmZvcm1hdGlvbiBvZiBuZXV0cmFsaXppbmcgYW50aWJvZHkgKE5BYikgc2VxdWVuY2VzLgojIEhlcmUgaXMgYW4gZXhhbXBsZSBvZiB0aGUgQ29yb25hdmlydXMgQW50aWJvZHkgRGF0YWJhc2UgKENvVi1BYkRhYikuCkNvVl9BYkRhYiA8LSByZWFkLmNzdigiZXhhbXBsZS9Db1YtQWJEYWJfMTMwNjIzLmNzdiIpCgojIyAzLjUuMiBOQWIgcmF0aW8gY2FsY3VsYXRpb24KIyBOQWIgcmF0aW8gaXMgZXN0YWJsaXNoZWQgYXMgYW4gaW5kaWNhdG9yIG9mIHRoZSBwcm9wb3J0aW9uYWwgcHJldmFsZW5jZSBvZiBuZXV0cmFsaXppbmcgYW50aWJvZHkgc2VxdWVuY2VzIHdpdGhpbiBleHBhbmRlZCBjbG9uYWxmYW1pbGllcyBpbiBlYWNoIHNhbXBsZS4KIyBJdCBpcyBkZWZpbmVkIGFzIHRoZSBmcmFjdGlvbiBvZiB0aGUgbnVtYmVyIG9mIE5BYiBzZXF1ZW5jZXMgd2l0aGluIGNsb25hbCBmYW1pbGllcyB0byB0aGUgdG90YWwgbnVtYmVyIG9mIE5BYiBzZXF1ZW5jZXMgcHJlc2VudCBpbiBlYWNoIHNhbXBsZS4KIyBOQWIgc2VxdWVuY2VzIGFyZSBxdWVyaWVkIHVzaW5nIE5BYi5xdWVyeSgpIGZ1bmN0aW9uIHdoZXJlIHRoZSBwYXJhbWV0ZXIgIm1ldGhvZCIgcmVwcmVzZW50cyB0aGUgQ0RSSDMgbWF0Y2hpbmcgbWV0aG9kLgojIEl0IGNhbiBiZSAiTkEiIGZvciBwZXJmZWN0IG1hdGNoLCAiaGFtbWluZyIgZm9yIGhhbW1pbmcgZGlzdGFuY2Ugb3IgImx2IiBmb3IgTGV2ZW5zaHRlaW4gZGlzdGFuY2UuIERlZmF1bHRzIHRvICJOQSIuCiMgVGhlIHBhcmFtZXRlciAic3BlY2llcyIgY2FuIGJlICJNb3VzZSIgb3IgIkh1bWFuIi4gRGVmYXVsdHMgdG8gIkh1bWFuIi4KIyBUaGUgcGFyYW1ldGVyICJtYXhEaXN0IiByZXByZXNlbnRzIHRoZSBtYXhpbXVtIGRpc3RhbmNlIGFsbG93ZWQgZm9yIG1hdGNoaW5nIHdoZW4gdGhlIGFyZ3VtZW50ICJtZXRob2QiIGlzICJoYW1taW5nIiBvciAibHYiLiBEZWZhdWx0cyB0byAiTkEiLgpOQWJfcmF0aW9fZGYgPC0gTkFiLnJhdGlvLmNhbGN1bGF0aW9uKHByb19kYXRhX2xpc3QxID0gQ09WSURfcHJvX2RhdGFfbGlzdCwgY2x1c3RlcnNfbGlzdDEgPSBDT1ZJRF9jbHVzdGVyc19saXN0LCBncm91cDFfbGFiZWwgPSAiQ09WSUQtMTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb19kYXRhX2xpc3QyID0gSENfcHJvX2RhdGFfbGlzdCwgY2x1c3RlcnNfbGlzdDIgPSBIQ19jbHVzdGVyc19saXN0LCBncm91cDJfbGFiZWwgPSAiSEMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFiRGFiID0gQ29WX0FiRGFiLCBtZXRob2QgPSBOQSwgbWF4RGlzdCA9IE5BLCBzcGVjaWVzID0gIkh1bWFuIikKCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQgPSAyLCBmaWcud2lkdGggPSAyLjJ9CiMgQm94cGxvdCBzaG93aW5nIHRoZSBOQWIgcmF0aW9zIGJldHdlZW4gdGhlIHR3byBncm91cHMuCiMgU3RhdGlzdGljYWwgY29tcGFyaXNvbnMgYXJlIGNhcnJpZWQgb3V0IGJ5IHRoZSB0d28tc2lkZWQgV2lsY294b24gcmFuay1zdW0gdGVzdC4KTkFiLnJhdGlvLnBsb3QoTkFiX3JhdGlvX2RmKQpgYGAKCg==